using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using Quartz;
using Quartz.Impl.Matchers;
using Quartz.Impl.Triggers;

namespace Util.QuartzNet
{
    public class QuartzNetHelper
    {
        private static IScheduler scheduler;

        /// <summary>
        /// 启动调度器，如果调度已启动，则不做任务操作
        /// </summary>
        /// <param name="schedulerFactory">调度工厂</param>
        /// <returns></returns>
        private static async Task StartScheduler(ISchedulerFactory schedulerFactory)
        {
            scheduler = await schedulerFactory.GetScheduler();
            if (!scheduler.IsStarted)
            {
                await scheduler.Start();
            }
        }

        /// <summary>
        /// 添加作业
        /// </summary>
        /// <param name="schedulerFactory">调度工厂</param>
        /// <param name="jobName">作业名称</param>
        /// <param name="groupName">分组</param>
        /// <param name="desc">描述</param>
        /// <param name="corn">调试触发时间</param>
        /// <param name="para">参数</param>
        /// <returns></returns>
        public static async Task AddJob<T>(ISchedulerFactory schedulerFactory, string jobName, string groupName, string desc, string corn, List<KeyValuePair<string, object>> para) where T : IJob
        {
            if (!IsValidExpression(corn))
            {
                throw new Exception("请确认corn表达式是否有效");
            }
            //先判断name+group是否存在
            await StartScheduler(schedulerFactory);
            var jobKeys = await scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName));
            if (jobKeys.Count > 0 && jobKeys.Count(p => p.Name == jobName) > 0)
            {
                throw new Exception($"作业[{jobName}],分组[{groupName}] 已存在");
            }

            JobDataMap dataMap = new JobDataMap();
            foreach (var item in para)
            {
                dataMap.Add(item);
            }

            //创建作业
            IJobDetail jobDetail = JobBuilder.Create<T>()
                .WithIdentity(jobName, groupName)
                .WithDescription(desc)
                .SetJobData(dataMap)
                .Build();

            //创建触发器
            ITrigger trigger = TriggerBuilder.Create()
                .WithIdentity(jobName, groupName)
                .WithDescription(desc)
                .WithCronSchedule(corn)
                .Build();

            //添加作业到调试器
            await scheduler.ScheduleJob(jobDetail, trigger);
        }

        /// <summary>
        /// 判断作业是否存在
        /// </summary>
        /// <param name="schedulerFactory">调度</param>
        /// <param name="jobName">作业名称</param>
        /// <param name="groupName">分组</param>
        /// <returns></returns>
        public static async Task<bool> ExistJob(ISchedulerFactory schedulerFactory, string jobName, string groupName)
        {
            //先判断name+group是否存在
            await StartScheduler(schedulerFactory);
            var jobKeys = await scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName));
            return jobKeys.Count > 0 && jobKeys.Count(p => p.Name == jobName) > 0;
        }

        /// <summary>
        /// 启动作业
        /// </summary>
        /// <param name="schedulerFactory">调度工厂</param>
        /// <param name="jobName">作业名称</param>
        /// <param name="groupName">分组</param>
        /// <returns></returns>
        public static async Task StartJob(ISchedulerFactory schedulerFactory, string jobName, string groupName)
        {
            await StartScheduler(schedulerFactory);
            var jobKeys = await scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName));
            if (jobKeys.Count > 0 && jobKeys.Count(p => p.Name == jobName) > 0)
            {
                var jobKey = jobKeys.FirstOrDefault(p => p.Name == jobName);
                await scheduler.ResumeJob(jobKey);
            }
            else
            {
                throw new Exception("作业不存在，请确认后再试");
            }
        }

        /// <summary>
        /// 暂停作业
        /// </summary>
        /// <param name="schedulerFactory">调度工厂</param>
        /// <param name="jobName">作业名称</param>
        /// <param name="groupName">分组</param>
        /// <returns></returns>
        public static async Task PauseJob(ISchedulerFactory schedulerFactory, string jobName, string groupName)
        {
            await StartScheduler(schedulerFactory);
            var jobKeys = await scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName));
            if (jobKeys.Count > 0 && jobKeys.Count(p => p.Name == jobName) > 0)
            {
                var jobKey = jobKeys.FirstOrDefault(p => p.Name == jobName);
                await scheduler.PauseJob(jobKey);
            }
            else
            {
                throw new Exception("作业不存在，请确认后再试");
            }
        }

        /// <summary>
        /// 更新作业
        /// </summary>
        /// <param name="schedulerFactory">调度工厂</param>
        /// <param name="jobName">作业名称</param>
        /// <param name="groupName">分组</param>
        /// <param name="desc">描述</param>
        /// <param name="corn">触发时间</param>
        /// <param name="para">参数</param>
        /// <returns></returns>
        public static async Task UpdateJob<T>(ISchedulerFactory schedulerFactory, string jobName, string groupName, string desc, string corn, List<KeyValuePair<string, object>> para) where T : IJob
        {
            if (!IsValidExpression(corn))
            {
                throw new Exception("请确认corn表达式是否有效");
            }
            await RemoveJob(schedulerFactory, jobName, groupName);
            //await AddJob<T>(schedulerFactory, jobName, groupName, desc, corn, para);
        }

        /// <summary>
        /// 移除作业
        /// </summary>
        /// <param name="schedulerFactory">调度工厂</param>
        /// <param name="jobName">作业名称</param>
        /// <param name="groupName">分组名称</param>
        /// <returns></returns>
        public static async Task RemoveJob(ISchedulerFactory schedulerFactory, string jobName, string groupName)
        {
            await StartScheduler(schedulerFactory);

            var jobKeys = await scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName));
            if (jobKeys.Count > 0 && jobKeys.Count(p => p.Name == jobName) > 0)
            {
                var jobKey = jobKeys.FirstOrDefault(p => p.Name == jobName);
                await scheduler.PauseJob(jobKey);//先暂停Job
                await scheduler.DeleteJob(jobKey);//再移除Job
            }
        }

        /// <summary>
        /// 立刻执行Job
        /// </summary>
        /// <param name="schedulerFactory">调度厂区</param>
        /// <param name="jobName">作业</param>
        /// <param name="groupName">分组</param>
        /// <returns></returns>
        public static async Task ExecuteJob(ISchedulerFactory schedulerFactory, string jobName, string groupName)
        {
            await StartScheduler(schedulerFactory);
            var jobKeys = await scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName));
            if (jobKeys.Count > 0 && jobKeys.Count(p => p.Name == jobName) > 0)
            {
                var jobKey = jobKeys.FirstOrDefault(p => p.Name == jobName);
                await scheduler.TriggerJob(jobKey);
            }
            else
            {
                throw new Exception("作业不存在，请确认后再试");
            }
        }

        /// <summary>
        /// 判断corn表达式是否合格
        /// </summary>
        /// <param name="cronExpression">corn表达式</param>
        /// <returns></returns>
        public static bool IsValidExpression(string cronExpression)
        {
            CronTriggerImpl trigger = new CronTriggerImpl();
            try
            {
                trigger.CronExpressionString = cronExpression;
                DateTimeOffset? date = trigger.ComputeFirstFireTimeUtc(null);
                return date != null;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// 获得触发器的状态
        /// </summary>
        /// <param name="scheduler">调度</param>
        /// <param name="key">触发器的KEY</param>
        /// <returns></returns>
        public static async Task<string> GetTriggerStatus(IScheduler scheduler, TriggerKey key)
        {
            TriggerState ts = await scheduler.GetTriggerState(key);
            string jobStatus = "";
            switch (ts)
            {
                case TriggerState.Normal:
                    jobStatus = "0";
                    break;
                case TriggerState.Paused:
                    jobStatus = "1";
                    break;
                case TriggerState.Complete:
                    jobStatus = "2";
                    break;
                case TriggerState.Error:
                    jobStatus = "3";
                    break;
                case TriggerState.Blocked:
                    jobStatus = "4";
                    break;
                case TriggerState.None:
                    jobStatus = "5";
                    break;
                default:
                    break;
            }
            return jobStatus;
        }
    }
}
